package omg.SongHandling.DataHandling;

import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import omg.Error.NoConnectionError;
import omg.Error.NoValidStreamError;
import omg.Error.ParserCreationFailedError;


public class StreamManager {

        private static String streamPackage = "omg.SongHandling.DataHandling";
	private static String[] registeredStreams = {"StreamMp4OverHttp", "StreamSwfOverHttp", "StreamFlvFromYouTube"};
	private static StreamManager instance = null;

	/* SINGLETON PATTERN */
        private StreamManager() {}
	public static StreamManager getInstance() {
            if (null == StreamManager.instance)
                StreamManager.instance = new StreamManager();
            return StreamManager.instance;
	}

	/**
	 * Iterates through all Streams known to the class and returns the first
         * one feeling responsible for the supplied Song.
	 * @param song the song to find a stream for
         * @return the suitable Stream initiated with the song
         * @throws NoValidStreamError if no fitting Stream could be found
         * @throws NoConnectionError if no connection to the uri target could be
         * established
	 */
	public Stream getStream(Song song) throws NoConnectionError, NoValidStreamError {
            for (String stream : StreamManager.registeredStreams) {
                try {
                    // get class to use dynamic method call and object creation
                    Class cl = Class.forName(streamPackage + '.' + stream);
                    if ((Boolean) cl.getMethod("isResponsibleFor", Song.class).invoke(null, song))
                        return (Stream) cl.getConstructor(Song.class).newInstance(song);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, stream + " constructor(Song) or isResponsibleFor(Song) is declared private/protected", ex);
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, stream + " constructor(Song) or isResponsibleFor(Song) requires wrong arguments", ex);
                } catch (InvocationTargetException ex) {
                    if (NoConnectionError.class == ex.getCause().getClass())
                        throw (NoConnectionError)ex.getCause();
                    else if (ParserCreationFailedError.class == ex.getCause().getClass())
                        Logger.getLogger("Periphery").log(Level.WARNING, stream + " constructor(Song) caused an controlled exception", ex.getCause());
                    else
                        Logger.getLogger("Periphery").log(Level.WARNING, stream + " constructor(Song) caused an unexpected exception", ex.getCause());
                } catch (NoSuchMethodException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, stream + " constructor(Song) or isResponsibleFor(Song) doesn't exist");
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, stream + " is registered as known parser, but the class couldn't be found");
                } catch (Exception ex) {
                    Logger.getLogger("Periphery").log(Level.WARNING, stream + " contains some not nearer specified error",  ex);
                }
            }
            throw new NoValidStreamError();
	}

}